// Project: Salient
// http://salient.codeplex.com
// 
// Copyright 2010, Sky Sanders <sky at skysanders.net>
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://salient.codeplex.com/license
// 
// Date: April 25 2010 

#region

using System;
using System.Collections;
using System.Collections.Generic;
using Salient.Collections.Generic;

#endregion




namespace Salient.Collections.Generic
{
    /// <summary>
    /// Represents a collection of keys and values.
    /// </summary>
    /// <typeparam name="TValue">
    /// The type of the values in the dictionary.
    /// </typeparam>
    public class SynonymousDictionary<TValue> : IDictionary<string, TValue>
    {
        private readonly Dictionary<string, TValue> _inner;
        private IDictionary<string, string> _synonyms;

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Collections.Generic.Dictionary`2" /> class 
        /// that is empty, has the default initial capacity, and uses the default equality comparer for the key type.
        /// </summary>
        public SynonymousDictionary()
            : this(0, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Collections.Generic.Dictionary`2" /> class that is 
        /// empty, has the default initial capacity, and uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer`1" />.
        /// </summary>
        /// <param name="comparer">
        /// The <see cref="T:System.Collections.Generic.IEqualityComparer`1" /> implementation to use when comparing 
        /// keys, or null to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1" /> for the type of the key.
        /// </param>
        public SynonymousDictionary(IEqualityComparer<string> comparer)
            : this(0, comparer)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Collections.Generic.Dictionary`2" /> class that is empty, has 
        /// the specified initial capacity, and uses the default equality comparer for the key type.
        /// </summary>
        /// <param name="capacity">
        /// The initial number of elements that the <see cref="T:System.Collections.Generic.Dictionary`2" /> can contain.
        /// </param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// <paramref name="capacity" /> is less than 0.
        /// </exception>
        public SynonymousDictionary(int capacity)
            : this(capacity, null)
        {
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Collections.Generic.Dictionary`2" /> class that 
        /// contains elements copied from the specified <see cref="T:System.Collections.Generic.IDictionary`2" /> and 
        /// uses the default equality comparer for the key type.
        /// </summary>
        /// <param name="dictionary">
        /// The <see cref="T:System.Collections.Generic.IDictionary`2" /> whose elements are copied to the 
        /// new <see cref="T:System.Collections.Generic.Dictionary`2" />.
        /// </param>
        /// <exception cref="T:System.ArgumentNullException">
        /// <paramref name="dictionary" /> is null.
        /// </exception>
        /// <exception cref="T:System.ArgumentException">
        /// <paramref name="dictionary" /> contains one or more duplicate keys.
        /// </exception>
        public SynonymousDictionary(IDictionary<string, TValue> dictionary)
            : this(dictionary, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Collections.Generic.Dictionary`2" /> class that contains 
        /// elements copied from the specified <see cref="T:System.Collections.Generic.IDictionary`2" /> and uses the 
        /// specified <see cref="T:System.Collections.Generic.IEqualityComparer`1" />.
        /// </summary>
        /// <param name="dictionary">
        /// The <see cref="T:System.Collections.Generic.IDictionary`2" /> whose elements are copied to the new 
        /// <see cref="T:System.Collections.Generic.Dictionary`2" />.
        /// </param>
        /// <param name="comparer">
        /// The <see cref="T:System.Collections.Generic.IEqualityComparer`1" /> implementation to use when comparing keys, 
        /// or null to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1" /> for the type of the key.
        /// </param>
        /// <exception cref="T:System.ArgumentNullException">
        /// <paramref name="dictionary" /> is null.
        /// </exception>
        /// <exception cref="T:System.ArgumentException">
        /// <paramref name="dictionary" /> contains one or more duplicate keys.
        /// </exception>
        public SynonymousDictionary(IDictionary<string, TValue> dictionary, IEqualityComparer<string> comparer)
            : this((dictionary != null) ? dictionary.Count : 0, comparer)
        {
            if (dictionary == null)
            {
                throw new ArgumentNullException("dictionary");
            }
            foreach (KeyValuePair<string, TValue> pair in dictionary)
            {
                Add(pair.Key, pair.Value);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Collections.Generic.Dictionary`2" /> class that is empty, has 
        /// the specified initial capacity, and uses the specified <see cref="T:System.Collections.Generic.IEqualityComparer`1" />.
        /// </summary>
        /// <param name="capacity">
        /// The initial number of elements that the <see cref="T:System.Collections.Generic.Dictionary`2" /> can contain.
        /// </param>
        /// <param name="comparer">
        /// The <see cref="T:System.Collections.Generic.IEqualityComparer`1" /> implementation to use when comparing keys, or 
        /// null to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1" /> for the type of the key.
        /// </param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// <paramref name="capacity" /> is less than 0.
        /// </exception>
        public SynonymousDictionary(int capacity, IEqualityComparer<string> comparer)
        {
            _inner = new Dictionary<string, TValue>(capacity, comparer);
            _synonyms = new Dictionary<string, string>(comparer);
        }


        /// <summary>
        /// 
        /// </summary>
        public IDictionary<string, string> Synonyms
        {
            get { return _synonyms; }
            set { _synonyms = value; }
        }

        #region IDictionary<string,TValue> Members

        /// <summary>
        /// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <returns>
        /// The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </returns>
        public int Count
        {
            get { return _inner.Count; }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
        /// </summary>
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
        /// </returns>
        public bool IsReadOnly
        {
            get { return ((ICollection<KeyValuePair<string, TValue>>) _inner).IsReadOnly; }
        }

        /// <summary>
        /// Gets or sets the element with the specified key.
        /// </summary>
        /// <returns>
        /// The element with the specified key.
        /// </returns>
        /// <param name="key">The key of the element to get or set.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and <paramref name="key"/>
        /// is not found.</exception>
        /// <exception cref="T:System.NotSupportedException">The property is set and the <see cref="T:System.Collections.Generic.IDictionary`2"/> 
        /// is read-only.</exception>
        public virtual TValue this[string key]
        {
            get { return _inner[GetKey(key)]; }
            set { _inner[GetKey(key)] = value; }
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the 
        /// <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the keys of the object that implements 
        /// <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </returns>
        public virtual ICollection<string> Keys
        {
            get { return _inner.Keys; }
        }

        ///// <summary>
        ///// Initializes a new instance of the <see cref="T:System.Collections.Generic.Dictionary`2" /> class with serialized data.
        ///// </summary>
        ///// <param name="info">
        ///// A <see cref="T:System.Runtime.Serialization.SerializationInfo" /> object containing the information required to 
        ///// serialize the <see cref="T:System.Collections.Generic.Dictionary`2" />.
        ///// </param>
        ///// <param name="context">
        ///// A <see cref="T:System.Runtime.Serialization.StreamingContext" /> structure containing the source and destination of the 
        ///// serialized stream associated with the <see cref="T:System.Collections.Generic.Dictionary`2" />.
        ///// </param>
        // TODO: SynonymousDictionary(SerializationInfo info
        //protected SynonymousDictionary(SerializationInfo info, StreamingContext context)
        //{
        //    this.m_siInfo = info;
        //}

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the 
        /// <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.Generic.ICollection`1"/> containing the values in the object that implements 
        /// <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </returns>
        public ICollection<TValue> Values
        {
            get { return _inner.Values; }
        }

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/>
        /// is read-only.</exception>
        public void Add(KeyValuePair<string, TValue> item)
        {
            _inner.Add(GetKey(item.Key), item.Value);
        }

        /// <summary>
        /// Adds an element with the provided key and value to the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </summary>
        /// <param name="key">The object to use as the key of the element to add.</param>
        /// <param name="value">The object to use as the value of the element to add.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        /// <exception cref="T:System.ArgumentException">An element with the same key already exists in 
        /// the <see cref="T:System.Collections.Generic.IDictionary`2"/>.</exception>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/> 
        /// is read-only.</exception>
        public void Add(string key, TValue value)
        {
            _inner.Add(GetKey(key), value);
        }

        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> 
        /// is read-only.</exception>
        public void Clear()
        {
            _inner.Clear();
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
        /// </summary>
        /// <returns>
        /// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
        /// </returns>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
        public bool Contains(KeyValuePair<string, TValue> item)
        {
            KeyValuePair<string, TValue> newItem = new KeyValuePair<string, TValue>(GetKey(item.Key), item.Value);
            return ((ICollection<KeyValuePair<string, TValue>>) _inner).Contains(newItem);
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the specified key.
        /// </summary>
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the key; otherwise, false.
        /// </returns>
        /// <param name="key">The key to locate in the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        ///                 </param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.
        ///                 </exception>
        public bool ContainsKey(string key)
        {
            return _inner.ContainsKey(GetKey(key));
        }

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, 
        /// starting at a particular <see cref="T:System.Array"/> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied 
        /// from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param>
        /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception>
        /// <exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.                     
        ///                     -or-
        ///                     <paramref name="arrayIndex"/> is equal to or greater than the length of <paramref name="array"/>.
        ///                     -or-
        ///                     The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater 
        ///                     than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.
        ///                     -or-
        ///                     Type <paramref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.
        ///                 </exception>
        public void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
        {
            ((ICollection<KeyValuePair<string, TValue>>) _inner).CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        public IEnumerator<KeyValuePair<string, TValue>> GetEnumerator()
        {
            return _inner.GetEnumerator();
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <returns>
        /// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; 
        /// otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </returns>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
        public bool Remove(KeyValuePair<string, TValue> item)
        {
            KeyValuePair<string, TValue> newItem = new KeyValuePair<string, TValue>(GetKey(item.Key), item.Value);
            return ((ICollection<KeyValuePair<string, TValue>>) _inner).Remove(newItem);
        }

        /// <summary>
        /// Removes the element with the specified key from the <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </summary>
        /// <returns>
        /// true if the element is successfully removed; otherwise, false.  This method also returns false if <paramref name="key"/>
        /// was not found in the original <see cref="T:System.Collections.Generic.IDictionary`2"/>.
        /// </returns>
        /// <param name="key">The key of the element to remove.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"/>
        /// is read-only.</exception>
        public bool Remove(string key)
        {
            return _inner.Remove(GetKey(key));
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <returns>
        /// true if the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element 
        /// with the specified key; otherwise, false.
        /// </returns>
        /// <param name="key">The key whose value to get.</param>
        /// <param name="value">When this method returns, the value associated with the specified key, if the key is found; 
        /// otherwise, the default value for the type of the <paramref name="value"/> parameter. This parameter is passed uninitialized.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        public bool TryGetValue(string key, out TValue value)
        {
            return _inner.TryGetValue(GetKey(key), out value);
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string GetKey(string key)
        {
            if (_synonyms.ContainsKey(key))
            {
                return _synonyms[key];
            }

            return key;
        }
    }
}